%{
import Lexico.AnalizadorLexico;
import Mensajes.Mensaje;
import PolacaInversa.Polaca;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Observable;
import TablaDeSimbolos.TSimbolo;
import TokensDelLenguaje.*;
import java.util.ArrayList;
import java.util.Iterator;


%}

%token ASIG MAYOR_IGUAL MENOR_IGUAL DIFERENTE IF ELSE THEN FOR PRINT ULONGINT UINTEGER CONST_UI CONST_UL CADENA IDENTIFICADOR BEGIN END STRUCT


%%
/* El programa consiste de declaraciones y bloque */
programa			:	declaraciones bloque {this.ts.clean();}
				|	bloque  {this.ts.clean();}
				|	declaraciones bloque error {yyerror("Programa: se esperaba fin de archivo luego de bloque principal");}
				|	declaraciones bloque_open {yyerror("Programa: se esperaba \"END\" luego de la ultima sentencia");}
				|	programaInvalido
				;

programaInvalido		:	';' {yyerror("El programa no respeta las sintaxis \"<declaraciones> <bloque>\" ");}
				|	declaraciones {yyerror("El programa no respeta las sintaxis \"<declaraciones> <bloque>\" ");}
				;

/* Declaracion de tipo estructura*/
tipo_estructura			: 	STRUCT IDENTIFICADOR '{' declaraciones '}' {yyout("Declaracion ESTRUCTURA " + $2.elemento_token.getCadena() + " como tipo");
										    
										    Token_Struct Est = new Token_Struct ($2.elemento_token.getCadena());
										    Est.agregarElementos($4.lista);
										    //Aca tendria que ver de eliminar los elementos de la tabla que pasan a ser parte de la estructura...
										    this.ts.eliminarTokens ($4.lista);
										    //this.ts.set(Est);
										    if (this.ts.set(Est)){
                                                                                                           yyerror("ERROR: Identificador duplicado->: " + Est.getCadena()); 
                                                
                                                                                    }
										    
										    ArrayList<Token> lista = new ArrayList<Token>();
										    lista.add(Est);
										    $$ = new ParserVal (lista);
										    }

				|	STRUCT  '{' declaraciones '}' {yyerror("Declaracion ESTUCTURA: falta nombre de la Estructura");}
				|	STRUCT IDENTIFICADOR '[' declaraciones ']' {yyerror("Declaracion ESTRUCTURA : se esperaba {} en lugar de []");}
				|	STRUCT IDENTIFICADOR '(' declaraciones ')' {yyerror("Declaracion ESTRUCTURA : se esperaba {} en lugar de ()");}
				|	STRUCT IDENTIFICADOR error {yyerror("Declaracion ESTRUCTURA: se esperaban llaves");}
				;

/* Aca reconoszo las declaraciones*/
declaraciones                   :	declaracion { $$ = $1; }
				|	declaraciones declaracion { ArrayList<Token> listad = $1.lista; listad.addAll($2.lista); $$ = new ParserVal (listad); }
				|	declaraciones error {yyerror("Declaracion: sentencia incorrecta");} declaracion
				;

/* Distintos tipo que se pueden declarar*/
declaracion			:	declaracion_ulongint ';' {$$ = $1; }
				|       declaracion_uinteger ';' {$$ = $1; }
				|	tipo_estructura ';' {$$ = $1; }
				|	IDENTIFICADOR declaracion_estructura ';' 
					{
					
					yyout("Declaracion de variables del tipo ESTRUCTURA " + $1.elemento_token.getCadena());
					    
					//Ver que exista la definicion del la estructura con ese nombre en la tabla de simbolos, si no error
					String nombreIdentificador = $1.elemento_token.getCadena();
					

					if ((this.ts.existeToken(nombreIdentificador)) && (this.ts.get(nombreIdentificador).getId().equals(Tokens.struct))){
					    
					    //Crear uno nuevo y clonarlo!!! al tipo de la tabla con el nombre de estructura nombreIdentificador
	
					   ArrayList variablesTipoEst = new ArrayList (); 
					   for(Iterator it = $2.lista.iterator(); it.hasNext(); ){
						    
						Token tipoEstructura = (Token_Struct)((Token_Struct)this.ts.get(nombreIdentificador)).clone();
						Token e = (Token) it.next();
						tipoEstructura.setCadena(e.getCadena());
						tipoEstructura.setPadre((Token_Identificador)this.ts.get(nombreIdentificador));
						//this.ts.set(tipoEstructura);
						if (this.ts.set(tipoEstructura)){
                                                                                    yyerror("ERROR: Identificador duplicado->: " + tipoEstructura.getCadena()); 
                                                
                                                }
						variablesTipoEst.add(tipoEstructura);

						}
		
					   $$ = new ParserVal (variablesTipoEst);						    
					   }else{
						  
							yyerror ("ERROR SINTACTICO: Tipo STRUCT o Tipo: " + nombreIdentificador + " no EXISTE");					
							ArrayList error = new ArrayList ();
							error.add($1.elemento_token);							
							$$ = new ParserVal (error);
						 };
					
					}

				;

/* Declaracion de variables de un tipo estructura ej: Pepe p1,p2; */
declaracion_estructura		:	IDENTIFICADOR  { 
					  		ArrayList listaTokens = new ArrayList();
					    		listaTokens.add($1.elemento_token);
					    		//La Izquierdo se vuelve lado derecho...
					    		$$ = new ParserVal(listaTokens);
					    		}

				|	declaracion_estructura ',' IDENTIFICADOR {
										  $1.lista.add($3.elemento_token);
										  $$ = $1;
										 }
				;

/* Declaracion de un ULONGINT, ej: ULONGINT a,b,c; */
declaracion_ulongint            :	ULONGINT IDENTIFICADOR {yyout("Declaracion ULONGINT");

					    //Obtengo el nombre la cadena del Identificador que va a ser el nombre de la Variable UInteger
					    String nombreIdentificador = $2.elemento_token.getCadena();
					    
					    //Creo un entero del tipo correspondiente
					    Token entero = new Token_Ulongint();
					    
					    //Creo variable con el nombre Identificador y un tipo entero sin valor, dado que no fue aun asignado
					    Token variable = new Token_Variable(nombreIdentificador, entero);
					    
					    //Actualizo la tabla de simbolos
					    //this.ts.set(variable);
					    if (this.ts.set(variable)){
                                                                        yyerror("ERROR SINTACTICO: Identificador duplicado->: " + variable.getCadena()); 
                                                
                                            }

					    
					    //Agrego en una lista de elementos auxiliar...
					    ArrayList listaTokens = new ArrayList();
					    listaTokens.add(variable);
					    
					    //La Izquierda se vuelve lado derecho...
					    $$ = new ParserVal(listaTokens);
				}

				|	declaracion_ulongint ',' IDENTIFICADOR {yyout("Declaracion ULONGINT");

					    //Obtengo el nombre la cadena del Identificador que va a ser el nombre de la Variable UInteger
					    String nombreIdentificador = $3.elemento_token.getCadena();
					    
					    //Creo un entero del tipo correspondiente
					    Token entero = new Token_Ulongint();
					    
					    //Creo variable con el nombre Identificador y un tipo entero sin valor, dado que no fue aun asignado
					    Token variable = new Token_Variable(nombreIdentificador, entero);
					    
					    //Actualizo la tabla de simbolos
					    //this.ts.set(variable);
					    if (this.ts.set(variable)){
                                                                        yyerror("ERROR SINTACTICO: Identificador duplicado->: " + variable.getCadena()); 
                                                
                                            }
					    
					    //Obtengo la lista y agrego el nuevo elemento...
					    $1.lista.add(variable);
					    
					    //El no terminal Izquierdo se vuevle los elementos del derecho...
					    $$ = $1;

				}
 
				|	declaracion_ulongint ',' {yyerror("Declaracion ULONGINT: se esperaba identificador luego de \",\"");}
				|	ULONGINT {yyerror("Declaracion ULONGINT: se esperaba identificador luego de palabra reservada ULONGINT");}
				;



/* Declaracion de un UINTEGER, ej: UINTEGER a,b,c; */
declaracion_uinteger            :	UINTEGER IDENTIFICADOR {yyout("Declaracion INTEGER");

					    //Obtengo el nombre la cadena del Identificador que va a ser el nombre de la Variable UInteger
					    String nombreIdentificador = $2.elemento_token.getCadena();
					    
					    //Creo un entero del tipo correspondiente
					    Token entero = new Token_Uinteger();
					    
					    //Creo variable con el nombre Identificador y un tipo entero sin valor, dado que no fue aun asignado
					    Token variable = new Token_Variable(nombreIdentificador, entero);
					    
					    //Actualizo la tabla de simbolos
					    //this.ts.set(variable);
					    if (this.ts.set(variable)){
                                                                         yyerror("ERROR SINTACTICO: Identificador duplicado -> " + variable.getCadena()); 
                                                
                                            }
					    //Agrego en una lista de elementos auxiliar...
					    ArrayList listaTokens = new ArrayList();
					    listaTokens.add(variable);
					    
					    //La Izquierda se vuelve lado derecho...
					    $$ = new ParserVal(listaTokens);
				}

				|	declaracion_uinteger ',' IDENTIFICADOR {yyout("Declaracion INTEGER");

					    //Obtengo el nombre la cadena del Identificador que va a ser el nombre de la Variable UInteger
					    String nombreIdentificador = $3.elemento_token.getCadena();
					    
					    //Creo un entero del tipo correspondiente
					    Token entero = new Token_Uinteger();
					    
					    //Creo variable con el nombre Identificador y un tipo entero sin valor, dado que no fue aun asignado
					    Token variable = new Token_Variable(nombreIdentificador, entero);
					    
					    //Actualizo la tabla de simbolos
					    //this.ts.set(variable);
					    if (this.ts.set(variable)){
                                                                        yyerror("ERROR SINTACTICO: Identificador duplicado -> " + variable.getCadena()); 
                                                
                                            }
					    //Obtengo la lista y agrego el nuevo elemento...
					    $1.lista.add(variable);
					    
					    //El no terminal Izquierdo se vuevle los elementos del derecho...
					    $$ = $1;

				}

				|	declaracion_uinteger ',' {yyerror("Declaracion INTEGER: se esperaba identificador luego de \",\"");}
				|	UINTEGER {yyerror("Declaracion INTEGER: se esperaba identificador luego de palabra reservada ULONGINT");}
				;


/* Definicion de cuerpo Bloque*/
bloque				:	sentencia 
				|	bloque_open END			
				|	bloque_open error {yyerror("Bloque: sentencia incorrecta");} END
				;

/* Apertura de Bloque*/
bloque_open			:	BEGIN 
				|	bloque_open bloque 
				;

/* Formacion de sentencias */
sentencia			:	asignacion ';' 
				|	asignacion {yyerror("Asignacion: se esperaba \";\"");} "\n"
				|	seleccion 
				|	iteracion 
				|	impresion ';' 
				|	impresion {yyerror("Impresion: se esperaba \";\"");}
				|	impresion_err ';'
				;

/* Asignacion */
asignacion			:	elementoAsignable ASIG expresion {	yyout ("Asignacion");
                                                                     		P.addElementos($1.lista);
                                                                     		P.addElemento(new Token_Asignacion ());
										$$ = $1; /* para el for */
								     	  }					
				;

/* Seleccion IF (condicion) THEN bloque ELSE bloque todos con presencia oblgatoria */

seleccion			:	IF condicion THEN  {yyout("--- Seleccion IF ---");} seleccion_else 
				|	IF condicion {yyout("--- Seleccion IF ---"); yyerror("Seleccion: Se esperaba THEN luego de condicion");} seleccion_else
				|	IF error {yyerror("Seleccion: se esperaba condicion luego de palabra reservada IF");}
				;

seleccion_else			:	bloque
					      {
						int posicionPrincipioELSE = P.posActual()+3; P.escribir((Integer)P.desapilar(), new Token_Pos(posicionPrincipioELSE)); 							P.apilar(P.posActual()+1); 
						P.addElemento(new Token_Pos(-1)); P.addElemento(new Token (Tokens.salto_incondicional)); 
						
					      }						
					ELSE {yyout("--- Seleccion ELSE ---");} seleccion_fin
				|	ELSE {yyout("--- Seleccion ELSE ---"); yyerror("Seleccion: se esperaba bloque luego de condicion");}
				|	bloque error {yyout("--- Fin de seleccion ---"); yyerror("Seleccion: se esperaba palabra reservada ELSE luego de bloque");}
				|	error {yyout("--- Fin de seleccion ---"); yyerror("Seleccion: se esperaba bloque luego de condicion");}
				;

seleccion_fin			:	bloque {yyout("--- Fin de seleccion ---");
						int posicionFinIFELSE = P.posActual()+1;
						P.escribir((Integer)P.desapilar(),new Token_Pos(posicionFinIFELSE));
					       }
 
				|	error {yyout("--- Fin de seleccion ---"); yyerror("Seleccion: se esperaba bloque luego de palabra reservada ELSE");}
				;
/* elemento asignable */
elementoAsignable		:	IDENTIFICADOR { ArrayList listaT = new ArrayList ();
    							listaT.add($1.elemento_token);
    							$$ = new ParserVal (listaT);
							}
				|	elemento_estructura {$$ = $1;}
				;


/* Expresiones Aritmeticas */
expresion			:	termino
				|	expresion '+' termino {
								//HH Crear un Token + Polaca.escribir(Token +)
								P.addElemento (new Token_OperadorSuma());								
							      }
				|	expresion '-' termino {
								//HH Crear un Token - Polaca.escribir(Token -)
							        P.addElemento (new Token_OperadorResta());
							      }
				|	expresion '+' error {yyerror("Expresion: se esperaba un termino luego de \"+\"");}
				|	expresion '-' error {yyerror("Expresion: se esperaba un termino luego de \"-\"");}
				;

termino				:	factor
				|	termino '*' factor {
							    //HH Crear un Token * y Polaca.escribir(Token *)
							    P.addElemento (new Token_OperadorMul());
							   }
				|	termino '/' factor {
							    //HH Crear un Token / y Polaca.escribir(Token /)					    
							    P.addElemento (new Token_OperadorDiv());
							   }

				|	termino '*' error {yyerror("Termino: se esperaba un factor luego de \"*\"");}
				|	termino '/' error {yyerror("Termino: se esperaba un factor luego de \"/\"");}
				;

factor				:	IDENTIFICADOR {
							// Polaca.escribir(Token IDENTIFICADOR) 
							P.addElemento ($1.elemento_token);
						      }
				|	constante {
							// Polaca.escribir(Token constante)
							P.addElemento ($1.elemento_token);
						  }
				|	elemento_estructura {
							     //Polaca.escribir(Token ElementoEstrucutra)
							     P.addElementos($1.lista);
							    }  
				|	'(' expresion ')' 
				|	'{' expresion '}' {yyerror("Factor: se esperaba \"(\" y \")\" en lugar de \"{\" y \"}\"");}
				|	'(' expresion error {yyerror("Factor: se esperaba \")\" luego de expresion");}
				|	'(' error {yyerror("Factor: se esperaba expresion luego de \"(\"");}
				;

/* Acceso elemento estructura */
elemento_estructura		:	IDENTIFICADOR '.' campo_estructura {
									    ArrayList listaEst = new ArrayList();
									    listaEst.add($1.elemento_token);
									    listaEst.addAll($3.lista);
									    listaEst.add(new Token_Punto());
									    $$ = new ParserVal (listaEst);
									   }
				;

/* Acceso a elementos de estructuras de mas de un nivel */
campo_estructura		:	IDENTIFICADOR {
							ArrayList ElementosEstructura = new ArrayList ();
							ElementosEstructura.add($1.elemento_token);
							$$ = new ParserVal (ElementosEstructura);
						        
						       }
				|	campo_estructura '.' IDENTIFICADOR {
//HH Crear un Token '.' Polaca.escribir(campo_estrucutra), Polaca.escribir(Token ID), Polaca.escribir('.')
									   // Agergo X,b,.
									   $1.lista.add($3.elemento_token); 
									   $1.lista.add(new Token_Punto());
									   $$ = $1;
	
									   } 
				;

constante			:	CONST_UL { $$ = $1;}
				|	CONST_UI { $$ = $1;}
				;


/* Condicion Logica */
condicion			:	'(' expresion operador_logico expresion ')' 	{ 
											P.addElemento (new Token_Cmp());
											P.addElemento (new Token_Pos(-1111));
											P.apilar(P.posActual());
											P.addElemento ($3.elemento_token);
											}
				|	expresion operador_logico expresion {yyerror("Condicion: se esperaba \"(\" y \")\"");}
				|	'{' expresion operador_logico expresion '}' {yyerror("Condicion: se esperaba \"(\" y \")\" en lugar de \"{\" y \"}\"");}
				|	'(' expresion operador_logico expresion error {yyerror("Condicion: se esperaba \")\" luego de expresion");}
				|	'(' expresion operador_logico error {yyerror("Condicion: se esperaba expresion luego de operador logico");}
				|	'(' expresion error {yyerror("Condicion: se esperaba operador logico");}
				|	'(' error ')' {yyerror("Condicion: condición lógica no válida");}
                                ;
/*Operador Logico*/
operador_logico			:	'>' {$$ = new ParserVal(new Token(Tokens.menor_igual));}
				|	'<' {$$ = new ParserVal(new Token(Tokens.mayor_igual));}
				|	'=' {$$ = new ParserVal(new Token(Tokens.distinto));}
				|	MAYOR_IGUAL {$$ = new ParserVal(new Token(Tokens.menor));}
				|	MENOR_IGUAL {$$ = new ParserVal(new Token(Tokens.mayor));}
				|	DIFERENTE {$$ = new ParserVal(new Token(Tokens.igual));}
				;

/* Impresion PRINT (cadena) */
impresion			:	PRINT '(' CADENA ')' 	{
								yyout("Impresion (" + $3.elemento_token.getCadena() + ")");
								P.addElemento($3.elemento_token);
								P.addElemento(new Token_Print());
								}
				;

impresion_err			:	PRINT '[' CADENA ']' {yyerror("Impresion: se esperaba \"(\" y \")\" en lugar de \"[\" y \"]\"");}
				|	PRINT '{' CADENA '}' {yyerror("Impresion: se esperaba \"(\" y \")\" en lugar de \"{\" y \"}\"");}
				|	PRINT '(' CADENA error {yyerror("Impresion: se esperaba \")\" luego de cadena");}
				|	PRINT '(' error {yyerror("Impresion: se esperaba cadena luego de \"(\"");}
				|	PRINT error {yyerror("Impresion: se esperaba \"(\" luego de palabra reservada PRINT");}
				;

/* Iteracion */
iteracion			:	FOR '(' controlFor ')'  bloque {/* creo token_pos(con posicion comienzocondicion o 										sea desapilo) desapiloTokenPos y le seteo la 										posicion fin de bloque creo toquenBI  */
															
									yyout("Fin de Bloque FOR"); 
									int posicionCondicionPorFalso =	(Integer)P.desapilar(); 
									P.addElementos($3.lista);
						//P.addElemento(new Token_OperadorSuma());									
									P.addElemento(new Token_Pos((Integer)P.desapilar())); 
									P.addElemento(new Token (Tokens.salto_incondicional)); 
									P.escribir(posicionCondicionPorFalso, new Token_Pos(P.posActual()+1));
														
									}
				
                                ;
controlFor		:	asignacion {  P.apilar(P.posActual()+1); } ';' comparacion ';' incremento
				{
					ArrayList listaT = new ArrayList ();
					listaT.addAll($1.lista);
					listaT.addAll($6.lista);
					listaT.add(new Token_OperadorSuma());
					listaT.addAll($1.lista);
					listaT.add(new Token_Asignacion());
					$$ = new ParserVal (listaT);			
				}			
			;


incremento		:	entero {
					$$ = new ParserVal($1.lista);					
				       }
			;

comparacion		:	 entero '<' entero {/*creo CMP, apiloposDeTokenPos, creo token_pos, creoBLT*/
							P.addElementos($1.lista);
							P.addElementos($3.lista);
							P.addElemento(new Token_Cmp()); 
							P.addElemento(new Token_Pos(-1111)); 
							P.apilar(P.posActual()); 
							P.addElemento(new Token(Tokens.mayor_igual));
						    }
			;


/* Elemento identificador o CONST_UL */
entero				:	IDENTIFICADOR { ArrayList listaT = new ArrayList ();
    							listaT.add($1.elemento_token);
    							$$ = new ParserVal (listaT);
							/*P.addElemento($1.elemento_token);*/
						      }
				|	CONST_UL {ArrayList listaT = new ArrayList ();
    						  listaT.add($1.elemento_token);
					          $$ = new ParserVal (listaT);
						  /*P.addElemento ($1.elemento_token);*/
						 }
				|	CONST_UI {ArrayList listaT = new ArrayList ();
    						  listaT.add($1.elemento_token);
					          $$ = new ParserVal (listaT);
						  /*P.addElemento ($1.elemento_token);*/
						 }
				|	elemento_estructura {$$ = $1;}
				;

%%

AnalizadorLexico al;    // Analizador Lexico
TSimbolo ts;		// Tabla de Simbolos
Polaca P;            	// Polaca

public Parser(AnalizadorLexico al, TSimbolo ts)
{
  this.al = al;
  this.ts = ts;
  this.P = new Polaca(this.ts);
}

void yyout(String texto) {
    Mensaje msj = new Mensaje("linea " + al.get_num_lineas() + " - " + texto, Mensaje.SENT);
    setChanged();
    notifyObservers(msj);
}

void yyerror(String texto) {
    Mensaje msj = new Mensaje("ERROR SINTACTICO: linea " + al.get_num_lineas() + " - " + texto, Mensaje.INF);
    setChanged();
    notifyObservers(msj);
}


short yylex() throws FileNotFoundException, IOException{
    Token token = al.obtener_token();
    Mensaje msj = new Mensaje(token.toString()+"\n", Mensaje.TOKEN);
    setChanged();
    notifyObservers(msj);
    yylval = new ParserVal(token);
    return token.traducir();
}


Token getToken(ParserVal val){
    return val.elemento_token;
}

public Polaca getPolaca() {
        return this.P;
    }


